import re
#from peephole_optimizer import *

class Block:
    def __init__(self, index):
        self.index = index      # label = index
        self.instructions = []
        self.target = None


compiler_directives = []
all_blocks = []

# read in assembly code with \n filtered out and fill in list with
# compiler directives.
def read_file(filename):
    global compiler_directives
    new = []
    
    flag_ent = 0
    with open(filename) as f:
        content = f.readlines()
    f.close()
    for index, elem in enumerate(content):
        if flag_ent == 0:
            compiler_directives.append(elem.replace("\n", ""))

        else:
            new.append(elem.replace("\n", ""))
            
        if ".ent" in elem:
            flag_ent = 1
        
    return new

# write global variable compiler_directives also to file
def write_to_file(filename, lines):
    f = open(filename, 'w')
    for j in compiler_directives:
        f.write(j+"\n")
    for i in lines:
        f.write(i+"\n")
    f.close()


# bne and jump optimizations
def remove_redundant_branches(assembly):
    flag_jump = 0


    # A.  if line in assembly code contains j   $L3, $L3
    for index, elem in enumerate(assembly):
        if ("j\t$L" in elem):
            # if $L3 == $L3

            e1 = re.split(r'[\t ,]', elem)
            e2 = re.split(r'[\t ,]', assembly[index+1])
            e2 = e2[-1]
            if e1[2] == e2[:-1]:
#                print "tada", e1[2], e2[:-1]
                del assembly[index]


    # B. if line in assembly code contains $L5, $L3
    for index, elem in enumerate(assembly):
        # benchmark "dhrystone" contains 3 consecutive labels
        # no valid-index-check, because last line in assembly code can not be a label
        if ((("$L" and ":") in elem) and (("$L" and ":") in assembly[index+1]) and (("$L" and ":") in assembly[index+2])):
            a = elem                # $L5:
            b = assembly[index+1]   # $L3:
            c = assembly[index+2]
            assembly.remove(b)
            assembly.remove(c)
            # a, b and c are now without :, that is [:-1] chops off last char
            a = a[:-1]              # $L5
            b = b[:-1]              # $L5
            c = c[:-1]              # $L5
            lb = len(b)
            lc = len(c)
            # search b ($L3) through whole file, replace it with a ($L5)
            for i, e in enumerate(assembly):
                if b in e:
                    temp = re.split(r'[\t ,]', e)
                    if lb == 3 and temp[-1] == b:
#                        print "VOILA1"
                        assembly[i] = e.replace(b, a)
                    elif lb == 4 and temp[-1] == b:
#                        print "VOILA2"
                        assembly[i] = e.replace(b, a)
                    elif lb > 4 and temp[-1] == b:
#                        print "VOILA2"
                        assembly[i] = e.replace(b, a)
                if c in e:
                    temp = re.split(r'[\t ,]', e)
                    if lc == 3 and temp[-1] == c:
                        assembly[i] = e.replace(c, a)
#                        print "VOILA3"
                    elif lc == 4 and temp[-1] == c:
#                        print "VOILA4"
                        assembly[i] = e.replace(c, a)
                    elif lc > 4 and temp[-1] == c:
#                        print "VOILA4"
                        assembly[i] = e.replace(c, a)

        elif (("$L" and ":") in elem) and (("$L" and ":") in assembly[index+1]):
            a = elem                # $L5
            b = assembly[index+1]   # $L3
            assembly.remove(b)
            # a and b are now without :
            a = a[:-1]
            b = b[:-1]
            lb = len(b)
            # search b ($L3) through whole file, replace it with a ($L5)
            for i, e in enumerate(assembly):
                if b in e:
                    temp = re.split(r'[\t ,]', e)
                    # for replacing $L5
                    if lb == 3 and temp[-1] == b:
#                        print "VOILA5"
                        assembly[i] = e.replace(b, a)
                   # for replacing $L55
                    elif lb == 4 and temp[-1] == b:
#                        print "VOILA6",b,a
                        assembly[i] = e.replace(b, a)
                    # for replacing 
                    elif lb > 4 and temp[-1] == b:
#                        print "VOILA6",b,a
                        assembly[i] = e.replace(b, a)


    # C. optimize bne 
    for i, e in enumerate(assembly):
        if (("beq" in e) and (("$L" and ":") in assembly[i+2]) and
 ("\tj\t$L" in assembly[i+1])):

            # split to get the individual components of the beq instruction
            t1 = re.split(r'[\t ,]', e)

            if t1[-1] == assembly[i+2][:-1]:
#                print "VOILA7",t1,"\n",e,"\n",assembly[i+2]
                # jump following the bne
                a = assembly[i+1]

                # split to get the individual components of the j instruction
                t2 = re.split(r'[\t ,]', a)

                # replace target of beq with label of the next j
                assembly[i] = "\tbne "+t1[2]+","+t1[3]+","+t2[2]
                
                # and remove the jump instruction
                del assembly[i+1]




    # D. optimize $L35, j   $L44
    for index, elem in enumerate(assembly):
        # find a label with consecutive jump to another label
        if (("$L" and ":") in elem) and (("\tj\t$L" in assembly[index+1])):
            a = assembly[index+1]
            elem = elem[:-1]                # elem was $L71:, now $L71
            temp = re.split(r'[\t ,]', a)
            l = len(temp[-1])
            del assembly[index]
            del assembly[index]
#            print "VOILA8",elem,"temp",temp
            for i, e in enumerate(assembly):
                if (elem in e):
                    old = re.split(r'[\t ,]', e)
                    for t in old:
#                        print "t",t,"temp",temp[-1]
                        if t == elem:
#                            print "t",t,"temp",temp[-1],"e",e
                            assembly[i] = e.replace(t, temp[-1])













    return assembly

def last_line_in_block(line):
    if ((("bne" in line) or ("beq" in line) or
        ("\tj\t" in line) or ("bc1f" in line) or
        ("bc1t" in line)) and (not "\tj\t$31" in line)):
        return True
    else:
        return False

def label(line):
    if ("$L" and ":") in line:
        return True
    else:
        return False

def make_blocks(code):
    index = 1
    global all_blocks

    for i, instruc in enumerate(code):
        last = last_line_in_block(code[i-1])
        lab = label(instruc)

        # create new block if current line is label or if previous line
        # is branch/jump
        if lab or last:
            block = Block(index)
            all_blocks.append(block)
            index = index + 1
            block.instructions.append(instruc)

##            # if previous line of assembly code was a branch/jump
##            if last:
##                temp = re.split(r'[\t ,]', code[i-1])
##                print code[i-1],"temp",temp[-1]
##                block.target = temp[-1]
##            elif lab:
##                temp = re.split(r'[\t ,]', instruc)
##                print code[i-1],"temp",temp[-1]
##                block.target = temp[-1]

        else:
            block.instructions.append(instruc)

# 8 -> 33470 -> 3.5000000000

    print "before len_blocks",len(all_blocks)

def first_phase(filename):
    global all_blocks
    global compiler_directives

    assembly = read_file(filename)
    
    # remove redundant branches and write to new file
    no_redun = remove_redundant_branches(assembly)
    write_to_file("global_opt_"+filename, no_redun)

    # append compiler directives into global list of basic blocks.
    bloc = Block(0)
    bloc.instructions = compiler_directives
    all_blocks.append(bloc)    

    # make basic blocks
    make_blocks(no_redun)

    # testing purposes
#    for h in all_blocks:
#        print "index",h.index
#        for i in h.instructions:
#            print "\tinstruc",i

    print "after lengte",len(all_blocks)

#first_phase()
